{"id":"cmwbn493t001fnv2on9g7xbbq","slug":"Auto Artikel","judul":"auto-artikel","deskripsi":null,"parentId":null,"urut":9,"kategori":"web","tag":"[]","status":"draft","versi":null,"konsep":"# Sistem Artikel Otomatis (Zadv) — temuan & konsep\n\n**Status: HIDUP TAPI BERHENTI MENGHASILKAN.** Infra, kode, dan DB sehat. Yang menghalangi\nsatu kecocokan jam antara crontab dan gate di skrip. Sejak **3 November 2026** tidak ada\nartikel baru; per 11 November = **8 hari kosong**.\n\n## Keadaan hari ini\n\n| Hal | Nilai |\n|---|---|\n| Aplikasi Zadv | `zpromo.zomet.my.id` — `/api/health` → 200 `{\"ok\":true,\"service\":\"zadv\"}` |\n| Crontab | `0 0 * * *` → `docker exec af7wit npx tsx scripts/artikel-harian.ts` |\n| Gate jam di skrip | `artikel_jam` default `07:00` |\n| Tabel `Pengaturan` | **0 baris** (semua setelan memakai default kode) |\n| Artikel `published` | 66 · terakhir **3 Nov 07:25** |\n| `scheduled` menumpuk | **12** (19 Okt: 3 · 4 Nov: 4 · 6 Nov: 5) |\n| `draft` menggantung | **3** (15 Okt, tanpa `scheduledFor`) |\n| Commit artikel di `zomet-main` | terakhir **3 Nov** |\n\n## Akar masalahnya\n\n```\ncrontab   jalankan 00:00 WIB\ngate      if (nowWib.getUTCHours() < 7) return \"SKIP\"\n```\n\nJam 00 memang lebih kecil dari 07 → **setiap malam cron menolak dirinya sendiri sebelum\nmenyentuh apa pun.** Log membuktikan, delapan baris terakhir berturut-turut:\n\n```\nSKIP: belum waktunya (setting=07:00, now WIB=00:00)\n```\n\nJam terbit terakhir `07:25`, bukan `07:00` tepat — itu tanda artikel terakhir lahir dari\n**trigger manual**, bukan cron. Jadi sejak cron dipindah ke VPS, jamnya tidak pernah cocok.\n\nBukan salah satu pihak saja: crontab menyala tengah malam, skrip menunggu pukul tujuh, dan\nkarena keduanya disimpan di tempat berbeda (crontab sistem vs `Pengaturan` DB), tidak ada\nyang memberi tahu saat keduanya berselisih.\n\n## Yang SUDAH benar (jangan diutak-atik)\n\nDiperiksa langsung di container yang berjalan, bukan dari repo:\n\n```ts\nscheduledFor: { lte: todayEnd }          // catch-up backlog hari terlewat\norderBy: { scheduledFor: 'asc' }         // slot terlama dulu\nconst doneApps = new Set<number>()       // maks 1 artikel per app per run\nguard per slot (appId + scheduledFor)    // tidak terbit dobel\n```\n\nKeempatnya sudah terpasang. Artinya 12 `scheduled` yang menumpuk **akan mengalir sendiri**\nbegitu gate-nya lolos — sekitar 5 hari pada 5 app, tidak perlu dipublish manual.\n\nGenerate menarget `today + 3`; publish mengambil `scheduledFor <= akhir hari ini`. Selisih\nitu memang sengaja: artikel disiapkan tiga hari lebih awal, lalu terbit sesuai jadwalnya.\n\n## Dua konsep perbaikan\n\n### A. Selaraskan crontab (satu perintah, tanpa deploy)\n\n```\n0 0 * * *   →   0 7 * * *\n```\n\nCocok dengan default `artikel_jam = 07:00`. Sekali jalan, langsung hidup.\n\n**Kelemahan:** kalau jam diubah dari dashboard jadi `08:00`, sistem mati lagi dengan cara\nyang persis sama — dan tidak ada yang memberi tahu sampai artikel berhenti muncul.\nIni menambal gejalanya, bukan sebabnya.\n\n### B. Gate mengikuti setelan, bukan menunggu (REKOMENDASI)\n\n```\ncrontab   0 * * * *                                   (mengetuk tiap jam)\ngate      nowWib.getUTCHours() !== settingHour        (bukan <)\n```\n\nCron mengetuk tiap jam; hanya jam yang sama dengan setelan dashboard yang bekerja.\nUbah jam dari dashboard sesukanya, tidak ada yang perlu disentuh lagi.\n\nKenapa aman: ketukan yang tidak kena jam langsung `return` sebelum Gemini dipanggil, dan\nguard idempoten (`doneApps`, dedup per slot) membuat ketukan berulang murah — tidak ada\nartikel dobel, tidak ada biaya API.\n\n**Konsekuensi yang harus diterima:** cron berdetak 24× sehari. Itu memang tujuannya —\nsatu sumber kebenaran (dashboard) dan cron hanya pemicu bodoh yang tidak perlu tahu jam.\n\n## Tindakan lain yang perlu, terlepas dari A/B\n\n### 1. Lima app dicentang aktif tapi tidak punya URL\n\n```\nKOSONG : ZGold · ZBengkel · ZBilliar · Z-Resto · Z-Absen   (artikelAktif = true)\nJALAN  : Z1 Pos · ZXGym · Z-Rooms · ZX Parenting · ZX Pajak\n```\n\nQuery generate menyaring `url: { not: null }`, jadi kelimanya dilewati **diam-diam**.\nDashboard menampilkan 10 app aktif, yang benar-benar bekerja 5. Isi URL-nya, atau hapus\ncentangnya supaya setelan tidak menipu.\n\n### 2. Tiga draft 15 Oktober menggantung\n\nTanpa `scheduledFor`, sistem tidak akan pernah menyentuhnya. Buang, atau jadwalkan manual.\n\n### 3. Sebaran artikel tidak rata\n\n```\nZ1 Pos 15 · ZXGym 15 · Z-Rooms 15 · ZX Parenting 9 · ZX Pajak 7\nZGold 1 · ZBengkel 1 · ZBilliar 1 · Z-Resto 1 · Z-Absen 1\n```\n\nAngka 1 itu sisa seed awal, bukan hasil kerja sistem.\n\n## Cara memverifikasi (jangan percaya satu sumber)\n\nTiga sumber harus sepakat — halaman artikel yang tetap tampil **bukan** bukti cron jalan,\nkarena UI menyajikan data lama:\n\n```\n1. DB Zadv\n   SELECT status, count(*) FROM \"ArtikelDraft\" GROUP BY 1;\n   SELECT max(\"publishedAt\") FROM \"ArtikelDraft\";\n   SELECT count(*) FILTER (WHERE status='scheduled' AND \"scheduledFor\" < current_date)\n     FROM \"ArtikelDraft\";          -- backlog yang menumpuk\n\n2. Log cron\n   grep SELESAI /tmp/artikel-harian.log | tail -3\n   → \"SELESAI: published=N, generated=M, failed=0\" adalah satu-satunya tanda cron benar\n     bekerja. Tanpa baris ini, jangan sebut cron hidup.\n\n3. Repo zomet-main\n   git log origin/main --oneline | grep 'artikel scheduled'\n```\n\n## Pitfall yang sudah pernah menggigit\n\n| Gejala | Sebab | Catatan |\n|---|---|---|\n| Log penuh `permission denied /var/run/docker.sock`, tanpa satu pun `SELESAI` | user `djvpri` dulu tidak ada di grup `docker` | **sudah beres** — `groups djvpri` kini memuat `docker`. Baris itu sisa riwayat, bukan masalah sekarang |\n| Log penuh `SKIP: belum waktunya` | crontab 00:00 vs gate 07:00 | **belum beres** — inilah yang dibahas |\n| Artikel terbit tapi tidak tampil di homepage konsumen | section memakai `{artikel.length > 0 && ...}`, dan fetch `!res.ok → []` | 503 Zadv menghapus section tanpa error; cek `/api/artikel/publik?appId=N` dulu |\n| `Bad credentials` saat push | token `ZOMET_PUSH_TOKEN` invalid atau tidak punya akses `zomet-main` | token valid tapi kurang izin → 403, bukan 401 |\n| `422 [SHA]: Required` dari Gitea | berkas BARU harus `POST`; `PUT` tanpa sha ditolak | atau token kurang scope tulis |\n\n## Urutan pengerjaan yang saya sarankan\n\n```\n1. Terapkan konsep B (crontab tiap jam + gate !==)\n2. Isi URL lima app kosong, atau hapus centangnya\n3. Buang tiga draft 15 Oktober\n4. Panggil trigger sekali → buktikan 12 scheduled mulai mengalir (maks 5/hari)\n5. Seminggu kemudian: pastikan max(publishedAt) bergerak tanpa disentuh\n```\n\nLangkah 4 penting: tanpa itu, perbaikan hanya terlihat sebagai \"tidak ada error\", bukan\nsebagai artikel yang benar-benar terbit.\n\n\n---\n\n## Status terkini (11 Nov 2026) — sudah diperbaiki\n\nKonsep **B** diterapkan dan terbukti bekerja: crontab jadi `0 * * * *`, gate di skrip\nmemakai `!==`, dan cron pukul 12:06 menghasilkan `SELESAI: published=5, generated=5, failed=0.`\nDB `published` 66 → 71, `max(\"publishedAt\")` bergerak ke 11 Nov, dan 5 commit\n`feat: artikel scheduled - ...` masuk ke `zomet-main`.\n\nRincian lengkap beserta bukti ada di bagian **9. HASIL**. Sisa pekerjaan: lima app tanpa URL,\ntiga draft 15 Oktober, dan alarm yang belum ada.\n","createdAt":"2026-11-11T04:50:02.340Z","updatedAt":"2026-11-11T05:24:37.876Z","bagian":[{"id":"cmwd5jgof0001nv8kk1y03tdm","blueprintId":"cmwbn493t001fnv2on9g7xbbq","judul":"1. Keadaan hari ini (12 Nov 2026)","konten":"Dokumentasi disusun dari pemeriksaan langsung pada **crontab user `djvpri`, container Zadv, dan DB produksi** — bukan dari membaca repo. Semua angka diambil **12 November 2026**.\n\n## Angka\n\n| Hal | Nilai | Sumber |\n|---|---|---|\n| Kesehatan Zadv | `200 {\"ok\":true}` | `/api/health` |\n| Crontab artikel | `0 * * * *` (user `djvpri`) | `crontab -u djvpri -l` |\n| Gate jam skrip | `jamWib >= settingHour` + berhenti bila hari ini sudah terisi | `scripts/artikel-harian.ts` |\n| Artikel `published` | **75** | `ArtikelDraft` |\n| Terbit terakhir | **10 Nov 2026 23:15** | `max(\"publishedAt\")` |\n| `scheduled` menunggu | **15** (13–16 Nov) | per tanggal di bawah |\n| Terlewat (jadwal lampau) | **0** | dijaga `batasAkhirHariWib()` |\n| Pool foto | **62** | `ArtikelFoto` |\n| App dipromosikan | **8** dari 14 (6 nonaktif) | `PromoApp.artikelAktif` |\n\n## Jadwal terbit berikutnya\n\n```\n13 Nov  scheduled 5\n14 Nov  scheduled 8\n15 Nov  scheduled 1\n16 Nov  scheduled 1\n```\n\nCatatan: 8 artikel pada 14 Nov adalah **8 app berbeda**, bukan 8 artikel untuk satu app. Batas harian berlaku per app.\n\n## Yang berubah sejak 11 Nov\n\nSistem ini sudah dua kali berhenti tanpa suara. Yang dijaga sekarang bukan hanya \"apakah jalan\", tetapi tiga hal berbeda: **apakah jalan**, **apakah hasilnya berguna**, dan **apakah kegagalan meninggalkan jejak**.\n","media":"[]","urut":0,"createdAt":"2026-11-12T06:13:31.310Z","updatedAt":"2026-11-12T06:13:31.310Z"},{"id":"cmwd5jgot0003nv8kkggtc4lg","blueprintId":"cmwbn493t001fnv2on9g7xbbq","judul":"2. Empat bug diperbaiki (12 Nov 2026)","konten":"Empat bug ditemukan dan diperbaiki **12 November 2026**, semuanya terpasang di container yang berjalan.\n\n## 1. Backlog yang terlewat tidak pernah terbit\n\n```ts\n// SEBELUM — batas \"hari ini\" dihitung zona proses (UTC)\nconst todayEnd = new Date(todayStart.getTime() + 24 * 3600 * 1000)\nscheduledFor: { lte: todayEnd }\n\n// SESUDAH — batas akhir hari WIB\nscheduledFor: { lt: batasAkhirHariWib() }\n```\n\nAkibat nyata: tiga draft berjadwal **18 Okt 17:00 WIB** (= 10:00 UTC) dianggap *masa depan* dan macet **25 hari**. Komentar di kode bahkan menjanjikan \"backlog hari yang terlewat ikut dikerjakan\" — janji itu tidak ditepati kodenya.\n\n## 2. Backlog menumpuk lalu terbit sekaligus\n\n```\n13 Nov : 5 artikel\n14 Nov : 8 artikel   ← terlihat seperti spam di /artikel\n```\n\nBatas lama hanya berlaku **per run**, bukan per hari. Sekarang dihitung dari artikel yang sudah terbit hari ini per app; parameter `tanpaBatasHarian` disediakan untuk kuras manual dari dashboard.\n\n## 3. Satu ketukan gagal menghapus seluruh hari\n\n```ts\n// SEBELUM — sama persis, tanpa percobaan ulang\nif (jamWib !== settingHour) return\n\n// SESUDAH — boleh mencoba lagi, berhenti hanya bila hari ini sudah terisi\nconst lewatJam = jamWib >= settingHour\nconst sudahHariIni = await prisma.artikelDraft.count({ where: { status: 'published', publishedAt: { gte: awalHariWib() } } })\nif (sudahHariIni > 0) return\n```\n\nLog lama sempat berbunyi `SKIP: jam WIB 08 bukan 07:00` empat kali sehari. Itu bukan perilaku yang salah — tetapi artinya **ketukan 07:00 yang gagal tidak punya cadangan**. Gemini 503 atau VPS sibuk cukup untuk menghapus satu hari penuh.\n\n## 4. Satu balasan Gemini cacat menghapus satu slot\n\n```\n✕ [AcuMedis] Bad escaped character in JSON at position 5283\n```\n\n`parseGeminiJson` kini membuang escape tidak sah (`\\\\x`, `\\\\u` tanpa 4 digit), lalu **mencoba ulang sekali** dengan prompt yang sama. Pesan galatnya menyertakan cuplikan 120 karakter di sekitar posisi gagal — dulu hanya nomor kolom tanpa isi, sehingga penyebabnya tidak bisa ditebak.\n\n## Angka hasil\n\n```\ntsc --noEmit            bersih\ntest-artikel-fix.test.ts 4 lulus\ncontainer Zadv          batasAkhirHariWib ✓  lewatJam >= ✓  coba ulang sekali ✓\n```\n\n---\n\n## Bagian yang DIPERTAHANKAN (masih berlaku)\n\n- **Structured data 8 halaman produk** — penanda `Article`/`Product` masih terpasang di `zomet-main`.\n- **Kanomik 91 artikel + logo publisher** — canonical dan `logo.png` sudah benar di produksi.\n- **Audit isi & putaran angle** — `angleIdx` masih dipakai; dedup per slot menjaga artikel kembar.\n- **Cara verifikasi** — lihat bagian berikut.\n","media":"[]","urut":1,"createdAt":"2026-11-12T06:13:31.326Z","updatedAt":"2026-11-12T06:13:31.326Z"},{"id":"cmwd5jgp10005nv8kv2vjnljl","blueprintId":"cmwbn493t001fnv2on9g7xbbq","judul":"3. Cron & alur satu ketukan","konten":"## Cron yang terpasang (user `djvpri`)\n\n```\n0 * * * *     artikel-harian.ts       ← mengetuk tiap jam, gate di dalam skrip\n0 8 * * 1     fakta-produk.ts          ← panen fakta produk, Senin 08:00\n0 */3 * * *   artikel-watchdog.sh      ← alarm bila >30 jam tanpa artikel baru\n0 */4 * * *   disk-watchdog.sh\n```\n\nCron artikel **tidak** di crontab `root` — milik user `djvpri`, alasan historis (akses docker). Kalau `crontab -l` sebagai root terlihat bersih dari artikel, itu **bukan** tanda cron hilang.\n\n## Kenapa mengetuk tiap jam, bukan `0 7 * * *`\n\nMengetuk tiap jam membuat jam bisa diubah dari dashboard **tanpa menyentuh crontab**, dan memberi percobaan ulang otomatis bila jam pertama gagal. Biayanya nol: ketukan yang tidak lolos gate berhenti sebelum memanggil Gemini.\n\n## Alur satu ketukan\n\n```\n1. Ambil setelan (artikel_jam, tujuan_zomet, tujuan_app)\n2. Gate: jamWib >= settingHour?  hari ini sudah ada artikel?  → berhenti bila tidak lolos\n3. publishScheduled()  ← kuras yang scheduledFor < besok 00:00 WIB\n4. generateSatuArtikel() per app, untuk hari+3\n5. Push ke zomet-main via Gitea\n```\n\nLangkah 3 mendahului 4 dengan sengaja: kalau Gemini mati, artikel yang **sudah ada** tetap terbit.\n","media":"[]","urut":2,"createdAt":"2026-11-12T06:13:31.333Z","updatedAt":"2026-11-12T06:13:31.333Z"},{"id":"cmwd5jgp50007nv8k2df6z7yz","blueprintId":"cmwbn493t001fnv2on9g7xbbq","judul":"4. App yang dipromosikan","konten":"Delapan app dipromosikan; enam dimatikan (`artikelAktif=false`) **tanpa dihapus** — datanya utuh untuk dihidupkan lagi.\n\n```\nAKTIF   ZX Pajak Kendaraan Kalbar · ZX Parenting · ZXGym · Z1 Pos\n        Z-Rooms · ZX Carwash · YTVault · AcuMedis\n\nMATI    ZGold · ZBengkel · ZBilliar · Z-Resto · Z-Absen\n        (domain curl 000, /produk/<slug> 404)\n\nBUKAN   Zomet (artikelAktif=false, induk situs — bukan produk)\n```\n\n## Syarat app boleh dipromosikan\n\n```\n1. PromoApp.url hidup (curl 200)\n2. PromoApp.halamanProduk ada dan tidak 404\n3. artikelAktif = true\n```\n\nKalau salah satu tidak terpenuhi, mesin akan menulis artikel yang menautkan ke halaman mati — dan itu merusak kepercayaan pembaca sekaligus SEO.\n\n## Cara menghidupkan app yang dimatikan\n\n```\n1. Nyalakan domain app (DNS + deploy)\n2. Pastikan /produk/<slug> hidup di zomet-main\n3. PromoApp: isi url + halamanProduk, set artikelAktif = true\n```\n","media":"[]","urut":3,"createdAt":"2026-11-12T06:13:31.338Z","updatedAt":"2026-11-12T06:13:31.338Z"},{"id":"cmwd5jgpc0009nv8ke54qmsmh","blueprintId":"cmwbn493t001fnv2on9g7xbbq","judul":"5. Tindakan yang masih perlu","konten":"| Gejala | Sebab | Status |\n|---|---|---|\n| Log penuh `permission denied /var/run/docker.sock`, tanpa satu pun `SELESAI` | user `djvpri` dulu tidak ada di grup `docker`; cron jalan sebagai `djvpri` | tetap waspada — cron artikel memang milik user `djvpri` |\n| `SKIP: jam WIB 08 bukan 07:00` berulang | gate `!==` lama; ketukan lain tidak punya fungsi apa pun | **diperbaiki 12 Nov** → `>=` + cek \"hari ini sudah terisi\" |\n| Tiga draft macet 25 hari padahal cron jalan | batas `todayEnd` dihitung UTC | **diperbaiki 12 Nov** → `batasAkhirHariWib()` |\n| Artikel terbit 7–8 sehari | batas hanya per run | **diperbaiki 12 Nov** → batas per app per hari |\n| `Bad escaped character in JSON` untuk satu app | balasan Gemini cacat, skrip menyerah | **diperbaiki 12 Nov** → bersihkan + coba ulang sekali |\n| Artikel kembar di slot yang sama | `Math.random()` untuk angle + tidak ada dedup | diperbaiki 11 Nov → `angleIdx` putaran + guard slot |\n| Halaman `/produk/<slug>` 404 padahal app aktif | lima app domain mati | diperbaiki 11 Nov → `artikelAktif=false` |\n| Deploy Zadv tetap `in_progress` di panel | bug pencatatan Coolify; log berakhir `Rolling update completed` | **periksa container**, jangan langsung deploy ulang |\n\n## Pelajaran yang berlaku umum\n\n```\nSetiap gate waktu harus punya SATU sumber kebenaran, atau\npunya alarm saat dua sumber itu berselisih.\n\nKegagalan yang keluar dengan exit 0 tidak akan pernah diberitakan\noleh cron, sistem monitoring, maupun log — yang terlihat hanya\n\"tidak ada artikel\", bukan \"cron menolak jalan\".\n```\n","media":"[]","urut":4,"createdAt":"2026-11-12T06:13:31.344Z","updatedAt":"2026-11-12T06:13:31.344Z"},{"id":"cmwd5jgpg000bnv8kut4baz28","blueprintId":"cmwbn493t001fnv2on9g7xbbq","judul":"6. Cara memverifikasi","konten":"Halaman artikel konsumen tetap tampil normal walau publisher mati berhari-hari (disajikan dari data lama). Jadi **\"halaman hidup\" bukan bukti job jalan.** Tiga sumber harus sepakat.\n\n## 1. Log cron\n\n```bash\ntail -30 /tmp/artikel-harian.log\n```\n\nYang dicari: baris `SELESAI: published=N`. Kalau hanya ada `SKIP`, gate menolak — dan itu **bukan** tanda sehat. Perhatikan juga apakah `published=0` padahal `generated>0` (artikel dibuat tetapi tidak terbit).\n\n## 2. DB Zadv\n\n```sql\nselect status, count(*) from \"ArtikelDraft\" group by status;\nselect count(*) from \"ArtikelDraft\" where status='scheduled' and \"scheduledFor\" < now() - interval '1 day';\n```\n\nAngka kedua harus **0**. Kalau tidak nol, backlog macet lagi — periksa `batasAkhirHariWib()` masih dipakai.\n\n## 3. Isi yang benar-benar terbit\n\n```sql\nselect d.judul, d.\"scheduledFor\", d.\"publishedAt\", a.nama\nfrom \"ArtikelDraft\" d join \"PromoApp\" a on a.id=d.\"appId\"\nwhere d.status='published' order by d.\"publishedAt\" desc limit 5;\n```\n\nCek: `appId` menunjuk app yang **punya halaman produk**, dan `publishedAt` hari ini.\n\n## 4. Watchdog\n\n`/root/artikel-watchdog.sh` (cron `0 */3 * * *`) mengirim WA bila **lebih dari 30 jam tidak ada artikel baru**, sekali per 12 jam. Alarm berbasis \"tidak ada artikel baru\", bukan \"ada backlog\" — saat menguras 12 slot tertunda, alarm backlog akan berbunyi palsu berhari-hari.\n","media":"[]","urut":5,"createdAt":"2026-11-12T06:13:31.349Z","updatedAt":"2026-11-12T06:13:31.349Z"},{"id":"cmwd5jgpn000dnv8kkffjsjeu","blueprintId":"cmwbn493t001fnv2on9g7xbbq","judul":"7. Riwayat pekerjaan","konten":"## 11 November 2026\n\n```\ncrontab artikel       0 0 * * *  →  0 * * * *        (user djvpri)\ngate jam              getUTCHours() < settingHour  →  !== settingHour\n5 app domain mati     artikelAktif = false (tidak dihapus)\n3 app hidup ditambah  ZX Carwash · YTVault · AcuMedis\nangle                 Math.random() → putaran + kolom angleIdx\ntautan internal       halamanProduk + UTM utm_source=artikel&utm_medium=blog\npanjang artikel       ditolak bila < 550 kata\nsitemap + robots      dibuat di zomet-main (98 URL)\npool foto             26 → 62\nwatchdog              >30 jam tanpa artikel baru → WA\nstructured data       Article/Product di 8 halaman produk\n```\n\n## 12 November 2026\n\n```\nbatas backlog         todayEnd (UTC) → batasAkhirHariWib()\nbatas harian          per run → per app per hari\ngate jam              !== → >= + berhenti bila hari ini terisi\nbalasan Gemini cacat  dibersihkan + coba ulang sekali + pesan bergalat cuplikan\n3 draft duplikat      dihapus (backup: /root/backup-artikeldraft-20261211-1234.sql)\n```\n\nYang **belum** dikerjakan dan masih terbuka:\n\n```\npool ArtikelIde masih kosong → ide dari pertanyaan pelanggan WA belum terpakai\nWaRiwayat 0 baris\nbatas \"1 artikel per hari untuk SELURUH situs\" (sekarang per app)\n```\n","media":"[]","urut":6,"createdAt":"2026-11-12T06:13:31.355Z","updatedAt":"2026-11-12T06:13:31.355Z"}],"children":[],"parent":null}